package com.hp.hpl.helper.misc;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreConnectionPNames;

import com.hp.hpl.helper.resource.FileHelper;

/**
 * 
 * @author fisher Zhang
 * add proxy support for post/get
 */
public class WebServiceHelper {
	
	private final static Log log = LogFactory.getLog(WebServiceHelper.class);
	private final static String SO_TIMEOUT = "http.socket.timeout"; 
	/**
	 * connectionTimeout->3 seconds
	 * socketTimeout->5 seconds
	 * @param serviceURL
	 * @return
	 */
	public static String getJsonResponse(String serviceURL) {
		return getJsonResponse(serviceURL,1*1000,5*1000);
	}
	
	/**
	 * @param serviceURL
	 * @param socketTimeout - in millisecond, it is the timeout for waiting for data.
	 * @return
	 */
	public static String getJsonResponse(String serviceURL,int connectionTimeout,int socketTimeout) {
		return getJsonResponse(serviceURL,null, connectionTimeout,socketTimeout);
	}
	
	
	/**
	 * @param serviceURL
	 * @param parameters
	 * @param connectionTimeout
	 * @param socketTimeout
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static String getJsonResponse(String serviceURL,Map<String,String> parameters,int connectionTimeout,int socketTimeout) throws IllegalArgumentException{
		return getJsonResponse(serviceURL,parameters,connectionTimeout,socketTimeout,null,-1);
	}

	/**
	 * @param serviceURL
	 * @param parameters
	 * @param connectionTimeout
	 * @param socketTimeout
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static String getJsonResponse(String serviceURL,Map<String,String> parameters,int connectionTimeout,int socketTimeout,String proxyServer,int proxyPort) throws IllegalArgumentException{

		DefaultHttpClient client = null;
		try {
			client = new DefaultHttpClient();
			//----------------------------------------
			setProxy(client,proxyServer,proxyPort);
			//----------------------------------------
			client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);

			List<NameValuePair> nameValuePair = null;
			if (parameters != null){
				nameValuePair = new ArrayList<NameValuePair>();
				for (Iterator<Map.Entry<String, String>> it = parameters.entrySet().iterator();it.hasNext();){
					Map.Entry<String, String> entry = it.next();
					if (entry.getKey()!=null && entry.getValue()!=null){
						//----------------------
						nameValuePair.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
						//----------------------
					}else{
						log.warn(":::[getJsonResponse]parameters: key|value is empty,key->"+entry.getKey()+",value.length->"+ (entry.getValue()==null?0:entry.getValue().length()));
					}
				}
			}
			String fullServiceURL = null;
			if (nameValuePair != null && nameValuePair.size()>0){
				fullServiceURL = serviceURL+"?"+URLEncodedUtils.format(nameValuePair, "UTF-8");
			}else{
				fullServiceURL = serviceURL;
			}
			//-----------------------------
			HttpGet getRequest = new HttpGet(fullServiceURL);
			
			getRequest.setHeader("Accept", "application/json");
			getRequest.setHeader("Content-type", "application/json");
			getRequest.getParams().setIntParameter(SO_TIMEOUT,socketTimeout); 
			HttpResponse response = client.execute(getRequest);
			HttpEntity entity = response.getEntity();
			if (response.getStatusLine() == null || response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
				if (entity != null) {
					log.error(IOHelper.toString(entity.getContent()));
				}
				throw new IllegalArgumentException(":::[getJsonResponse] failed! status line is " + response.getStatusLine());
			}
			InputStream is = entity.getContent();
			return IOHelper.toString(is);
		} catch (Exception ex){
			log.error(":::[getJsonResponse]web service error,serviceURL->"+serviceURL+",msg->"+ex.getMessage());
			throw new IllegalArgumentException(":::[getJsonResponse]WS failed, msg->"+ex.getMessage());
		} finally{
			if (client != null){
				client.getConnectionManager().shutdown();
			}
		}
			
	}
	
	/**
	 * @param serviceURL
	 * @param parameters
	 * @param connectionTimeout
	 * @param socketTimeout
	 * @param proxyServer
	 * @param proxyPort
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static byte[] getBinaryResponse(String serviceURL,Map<String,String> parameters,int connectionTimeout,int socketTimeout,String proxyServer,int proxyPort) throws IllegalArgumentException{
		DefaultHttpClient client = null;
		try {
			client = new DefaultHttpClient();
			//----------------------------------------
			setProxy(client,proxyServer,proxyPort);
			//----------------------------------------
			client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);

			List<NameValuePair> nameValuePair = null;
			if (parameters != null){
				nameValuePair = new ArrayList<NameValuePair>();
				for (Iterator<Map.Entry<String, String>> it = parameters.entrySet().iterator();it.hasNext();){
					Map.Entry<String, String> entry = it.next();
					if (entry.getKey()!=null && entry.getValue()!=null){
						//----------------------
						nameValuePair.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
						//----------------------
					}else{
						log.warn(":::[getBinaryResponse]parameters: key|value is empty,key->"+entry.getKey()+",value.length->"+ (entry.getValue()==null?0:entry.getValue().length()));
					}
				}
			}
			String fullServiceURL = null;
			if (nameValuePair != null && nameValuePair.size()>0){
				fullServiceURL = serviceURL+"?"+URLEncodedUtils.format(nameValuePair, "UTF-8");
			}else{
				fullServiceURL = serviceURL;
			}
			//-----------------------------
			HttpGet getRequest = new HttpGet(fullServiceURL);
			
			getRequest.setHeader("Accept", "*.*, q=0.1");
			getRequest.setHeader("Content-type", "application/octet-stream");
			getRequest.getParams().setIntParameter(SO_TIMEOUT,socketTimeout); 
			HttpResponse response = client.execute(getRequest);
			HttpEntity entity = response.getEntity();
			if (response.getStatusLine() == null || response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
				if (entity != null) {
					log.error(IOHelper.toString(entity.getContent()));
				}
				throw new IllegalArgumentException(":::[getBinaryResponse] failed! status line is " + response.getStatusLine());
			}
			InputStream is = entity.getContent();
			return FileHelper.readBytes(is);
		} catch (Exception ex){
			log.error(":::[getBinaryResponse]web service error,serviceURL->"+serviceURL+",msg->"+ex.getMessage());
			throw new IllegalArgumentException(":::[getBinaryResponse]WS failed, msg->"+ex.getMessage());
		} finally{
			if (client != null){
				client.getConnectionManager().shutdown();
			}
		}
	}
	/**
	 * @param serviceURL
	 * @param parameters<String,String> - to check whether you need encode the content of value.
	 * @param connectionTimeout - in millisecond
	 * @param socketTimeout - in millisecond
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static String postJsonRequest(String serviceURL,Map<String,String> parameters,int connectionTimeout,int socketTimeout) throws IllegalArgumentException{
		return postJsonRequest(serviceURL,parameters,connectionTimeout,socketTimeout,null,-1);
	}	
	
	/**
	 * @param serviceURL
	 * @param parameters
	 * @param connectionTimeout
	 * @param socketTimeout
	 * @param proxyServer - if null or empty string, do not use proxy setting
	 * @param port
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static String postJsonRequest(String serviceURL,Map<String,String> parameters,int connectionTimeout,int socketTimeout,String proxyServer, int proxyPort) throws IllegalArgumentException{

		DefaultHttpClient client = null;
		try {
			//TODO: this connection timeout does not work?
			//method 1
			//HttpParams params = new BasicHttpParams();
			//params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);
			//client = new DefaultHttpClient(params);
			
			//method 2
			client = new DefaultHttpClient();
			
			setProxy(client,proxyServer,proxyPort);
			
			client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);
			//-----------------------------
			
			HttpPost post = new HttpPost(serviceURL);
			List<NameValuePair> nameValuePair = null; 
			if (parameters != null){
				nameValuePair = new ArrayList<NameValuePair>();
				for (Iterator<Map.Entry<String, String>> it = parameters.entrySet().iterator();it.hasNext();){
					Map.Entry<String, String> entry = it.next();
					if (entry.getKey()!=null && entry.getValue()!=null){
						//----------------------
						nameValuePair.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
						//----------------------
					}else{
						log.warn(":::[postJsonRequest]parameters: key|value is empty,key->"+entry.getKey()+",value.length->"+ (entry.getValue()==null?0:entry.getValue().length()));
					}
				}
				UrlEncodedFormEntity requestEntity = new UrlEncodedFormEntity(nameValuePair,Charset.forName("utf-8"));
				post.setEntity(requestEntity);
			}
			//---------------------
			post.getParams().setIntParameter(SO_TIMEOUT,socketTimeout);
			//---------------------
			HttpResponse response = client.execute(post);
			HttpEntity responseEntity = response.getEntity();
			//---------------------------------------------
			//BUG FIX: use closed stream
			//if (responseEntity != null){
			//	responseEntity.consumeContent();  //close the stream
			//}
			//---------------------------------------------
			if (response.getStatusLine() == null || response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
				String msg = null;
				if (responseEntity != null) {
					msg = IOHelper.toString(responseEntity.getContent());
					log.error(msg);
				}
				throw new IllegalArgumentException("status line:" + response.getStatusLine()+",response->"+msg);
			}else{
				return IOHelper.toString(responseEntity.getContent());
			}
		} catch (Exception ex){
			ex.printStackTrace();
			log.error(":::[postJsonRequest]web service error,serviceURL->"+serviceURL+",msg->"+ex.getMessage());
			throw new IllegalArgumentException(":::[postJsonRequest]WS error, msg->"+ex.getMessage());
		} finally{
			if (client != null){
				client.getConnectionManager().shutdown();
			}
		}
	
		
	}
	
	/**
	 * if proxyServer is not empty, use proxy
	 * added by fisherZhang on 2011.6.29 10:16
	 * @param client
	 * @param proxyServer
	 * @param proxyPort
	 */
	private static void setProxy(DefaultHttpClient client,String proxyServer,int proxyPort){
		if (client == null){
			throw new IllegalArgumentException(":::[setProxy]client is null");
		}
		if (! StringHelper.isEmpty(proxyServer)){
			HttpHost proxy = new HttpHost(proxyServer, proxyPort);
			client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
		}
	}
	/**
	 * @param request
	 * @return
	 */
	public static String buildFullContextPath(HttpServletRequest request){
		return request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath();
	}
	
	/**
	 * @param downloadURL
	 * @param targetFilePath
	 * @return
	 */
	public static boolean download(String downloadURL,String targetFilePath) throws IllegalArgumentException{
        return download(downloadURL,targetFilePath,null,0);
	}
	/**
	 * connectionTimeout -> 3s
	 * socketTimeout-> 3 minutes
	 * bufferSize = 1024
	 * @param downloadURL
	 * @param targetFilePath
	 * @param connectionTimeout
	 * @param socketTimeout
	 * @param proxyServer
	 * @param proxyPort
	 * @return
	 */
	public static boolean download(String downloadURL,String targetFilePath, String proxyServer,int proxyPort) throws IllegalArgumentException{
		return download(downloadURL,targetFilePath,1024,3*1000, 3*60*1000,proxyServer,proxyPort);
	}
	/**
	 * download file from url, if directory didn't exists, create it automatically.
	 * can not download some special URL
	 * @param downloadURL
	 * @param targetFilePath
	 * @param bufferSize
	 * @param connectionTimeout
	 * @param socketTimeout
	 * @param proxyServer
	 * @param proxyPort
	 * @return
	 */
	@Deprecated
	private static boolean download_HttpClient(String downloadURL,String targetFilePath,int bufferSize, int connectionTimeout,int socketTimeout,String proxyServer,int proxyPort) throws IllegalArgumentException{
		StopWatch15 sw = new StopWatch15();
		DefaultHttpClient client = null;
		try {
			sw.start();
			client = new DefaultHttpClient();
			//----------------------------------------
			setProxy(client,proxyServer,proxyPort);
			//----------------------------------------
			client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);

			//-----------------------------
			HttpGet getRequest = new HttpGet(downloadURL);
			getRequest.getParams().setIntParameter(SO_TIMEOUT,socketTimeout); 
			HttpResponse response = client.execute(getRequest);
			if (log.isDebugEnabled()){
				log.debug(":::[download]execute response took:"+sw.getElapsedMsS());
			}
			
			InputStream in = response.getEntity().getContent();
			// create directory if didn't exist 
			FileHelper.makeSureDirExist(targetFilePath);
			//--------------------------------------
			OutputStream os = null;//new FileOutputStream(new File(targetFilePath));
			
			long totalSize = 0;
			byte[] b = new byte[bufferSize];
			int len = 0;
			int count = 0;
			while ((len = in.read(b)) != -1) {
				os.write(b, 0, len);
				totalSize += len;
				//performance is low
				count++;
				if (count == 20 && log.isDebugEnabled()){
					count = 0; //reset
					log.debug(":::[download]downloaded size->"+totalSize);
				}
			}
			in.close();
			os.close();
		} catch (Exception ex){
			String msg = ":::[download]download file failed,downloadURL->"+downloadURL+",msg->"+ex.getMessage(); 
			log.error(msg);
			throw new IllegalArgumentException(msg);
		} finally{
			if (client != null){
				client.getConnectionManager().shutdown();
			}
		}
		//-----------------------------------------
		if (log.isInfoEnabled()){
			sw.stop();
			log.info(":::[download]total time:"+sw.getElapsedMsS());
		}
		return true;
			
	}
	
	/**
	 * download via URLConnection.
	 * @param downloadURL
	 * @param targetFilePath
	 * @param bufferSize
	 * @param connectionTimeout
	 * @param socketTimeout
	 * @param proxyServer
	 * @param proxyPort
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static boolean download(String downloadURL, String targetFilePath, int bufferSize, int connectionTimeout, int socketTimeout, String proxyServer, int proxyPort) throws IllegalArgumentException {
		try {
			URL url = new URL(downloadURL);
			URLConnection conn = url.openConnection();
			conn.setConnectTimeout(connectionTimeout);
			conn.setReadTimeout(socketTimeout);
			InputStream is = conn.getInputStream();
			if ("gzip".equalsIgnoreCase(conn.getContentEncoding()))
				is = new BufferedInputStream(new GZIPInputStream(is));
			else
				is = new BufferedInputStream(is);
			FileHelper.writeFile(is, targetFilePath, bufferSize);
			is.close();
		} catch (Exception e) {
			log.error((new StringBuilder(":::[download]msg->")).append(e.getMessage()).toString());
			return false;
		}
		return true;
	}

}
